{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Particle Tracker\n",
    "================\n",
    "\n",
    "An example of PlasmaPy's particle tracker class.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import astropy.units as u\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "from plasmapy.formulary import ExB_drift, gyrofrequency\n",
    "from plasmapy.particles import Particle\n",
    "from plasmapy.plasma.grids import CartesianGrid\n",
    "from plasmapy.simulation.particle_tracker.particle_tracker import ParticleTracker\n",
    "from plasmapy.simulation.particle_tracker.save_routines import IntervalSaveRoutine\n",
    "from plasmapy.simulation.particle_tracker.termination_conditions import (\n",
    "    TimeElapsedTerminationCondition,\n",
    ")"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": "Take a look at the docs to :func:`~plasmapy.formulary.frequencies.gyrofrequency` and :class:`~plasmapy.simulation.particle_tracker.particle_tracker.ParticleTracker` for more information"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Initialize a :class:`~plasmapy.plasma.grids.CartesianGrid` object. This will be the source of electric and magnetic\n",
    "fields for our particles to move in.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "grid_length = 10\n",
    "grid = CartesianGrid(-1 * u.m, 1 * u.m, num=grid_length)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Initialize the fields. We'll take B in the x direction\n",
    "and E in the y direction, which gets us an E cross B drift\n",
    "in the negative z direction.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "Bx_fill = 4 * u.T\n",
    "Bx = np.full(grid.shape, Bx_fill.value) * u.T\n",
    "\n",
    "Ey_fill = 2 * u.V / u.m\n",
    "Ey = np.full(grid.shape, Ey_fill.value) * u.V / u.m\n",
    "\n",
    "grid.add_quantities(B_x=Bx, E_y=Ey)\n",
    "ExB_drift(\n",
    "    np.asarray([0, Ey_fill.value, 0]) * u.V / u.m,\n",
    "    np.asarray([Bx_fill.value, 0, 0]) * u.T,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "|ParticleTracker| takes arrays of particle positions and velocities of the shape [nparticles, 3], so these arrays represent one particle starting at the origin."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "x0 = [[0, 0, 0]] * u.m\n",
    "v0 = [[1, 0, 0]] * u.m / u.s\n",
    "particle = Particle(\"p+\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Initialize our stop condition and save routine. We can determine a relevant\n",
    "duration for the experiment by calculating the gyroperiod for the particle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "particle_gyroperiod = 1 / gyrofrequency(Bx_fill, particle).to(\n",
    "    u.Hz, equivalencies=u.dimensionless_angles()\n",
    ")\n",
    "\n",
    "simulation_duration = 100 * particle_gyroperiod\n",
    "save_interval = particle_gyroperiod / 10\n",
    "\n",
    "termination_condition = TimeElapsedTerminationCondition(simulation_duration)\n",
    "save_routine = IntervalSaveRoutine(save_interval)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Initialize the trajectory calculation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "simulation = ParticleTracker(\n",
    "    grid,\n",
    "    save_routine=save_routine,\n",
    "    termination_condition=termination_condition,\n",
    "    verbose=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "We still have to initialize the particle's velocity. We'll limit ourselves to\n",
    "one in the x direction, parallel to the magnetic field B -\n",
    "that way, it won't turn in the z direction.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "simulation.load_particles(x0, v0, particle)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run the simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "simulation.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can take a look at the trajectory in the z direction:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results = save_routine.results\n",
    "particle_trajectory = results[\"x\"][:, 0]\n",
    "particle_position_z = particle_trajectory[:, 2]\n",
    "\n",
    "plt.scatter(results[\"time\"], particle_position_z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "or plot the shape of the trajectory in 3D:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "tags": [
     "nbsphinx-thumbnail"
    ]
   },
   "outputs": [],
   "source": [
    "fig = plt.figure()\n",
    "ax = fig.add_subplot(projection=\"3d\")\n",
    "\n",
    "ax.plot(*particle_trajectory.T)\n",
    "ax.set_xlabel(\"X\")\n",
    "ax.set_ylabel(\"Y\")\n",
    "ax.set_zlabel(\"Z\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As a test, we calculate the mean velocity in the z direction from the\n",
    "velocity:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "v_mean = results[\"v\"][:, :, 2].mean()\n",
    "print(\n",
    "    f\"The calculated drift velocity is {v_mean:.4f} to compare with the \"\n",
    "    f\"expected E0/B0 = {-(Ey_fill / Bx_fill).value:.4f}\"\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
